// WIP
// ======================================================
// uDisplay_spi_panel.h - SPI LCD Panel Implementation
// ======================================================

#pragma once

#include "uDisplay_panel.h"
#include "uDisplay_SPI_controller.h"

typedef struct LVGL_PARAMS_t {
  uint16_t flushlines;
  union {
    uint8_t data;
    struct {
      uint8_t use_dma : 1;
      uint8_t swap_color : 1;
      uint8_t async_dma : 1;   // force DMA completion before returning, avoid conflict with other devices on same bus. If set you should make sure the display is the only device on the bus
      uint8_t busy_invert : 1;
      uint8_t invert_bw : 1;
      uint8_t resvd_3 : 1;
      uint8_t resvd_4 : 1;
      uint8_t resvd_5 : 1;
    };
  };
}LVGL_PARAMS_t;


/**
 * Configuration for SPI-based displays
 */
struct SPIPanelConfig {
    // ===== Display Dimensions =====
    uint16_t width;
    uint16_t height;
    uint8_t bpp;              // bits per pixel (1, 8, 16, etc.)
    uint8_t col_mode;         // color mode (16, 18, etc.)

    // ===== Address Window Protocol =====
    uint8_t cmd_set_addr_x;   // Command to set X address range
    uint8_t cmd_set_addr_y;   // Command to set Y address range  
    uint8_t cmd_write_ram;    // Command to write pixel data

    // ===== Display Control Commands =====
    uint8_t cmd_display_on;
    uint8_t cmd_display_off;
    uint8_t cmd_invert_on;
    uint8_t cmd_invert_off;
    uint8_t cmd_memory_access; // For rotation settings
    uint8_t cmd_startline;     // For vertical scroll offset

    // ===== Per-Rotation Configuration =====
    uint8_t rot_cmd[4];        // Memory access command variant for each rotation
    uint16_t x_addr_offset[4]; // Address offset per rotation
    uint16_t y_addr_offset[4];
    uint8_t address_mode;      // Addressing scheme (8, 16, 32-bit)

    // ===== Flags =====
    bool all_commands_mode;    // If true: send data bytes as commands

    // ===== Reset & Power Control =====
    int8_t reset_pin;          // GPIO for display reset (-1 if none)
    // int8_t busy_pin;        // REMOVED - busy_pin is EPD-only, moved to EPDPanelConfig
    int8_t bpanel;             // Backlight GPIO (-1 if none)
};

class SPIPanel : public UniversalPanel {
public:
    /**
     * Constructor - receives framebuffer from uDisplay if needed
     */
    SPIPanel(const SPIPanelConfig& config,
             SPIController* spi_ctrl,
             uint8_t* framebuffer);
    
    ~SPIPanel();
    
    // ===== UniversalPanel Interface =====
    bool drawPixel(int16_t x, int16_t y, uint16_t color) override;
    bool fillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color) override;
    bool pushColors(uint16_t *data, uint16_t len, bool not_swapped = false) override;
    bool setAddrWindow(int16_t x0, int16_t y0, int16_t x1, int16_t y1) override;
    bool drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color) override;
    bool drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color) override;
    
    bool displayOnff(int8_t on) override;
    bool invertDisplay(bool invert) override;
    bool setRotation(uint8_t rotation) override;
    bool updateFrame() override;

private:
    // ===== Hardware & Configuration =====
    SPIController* spi;            // Not owned by panel
    SPIPanelConfig cfg;            // Copy of config

    // ===== Framebuffer =====
    uint8_t* fb_buffer;            // Framebuffer (if provided by uDisplay)

    // ===== Display State =====
    uint8_t rotation;              // Current rotation (0-3)
    int16_t window_x0, window_y0, window_x1, window_y1;
    bool display_on;
    bool inverted;

    bool use_hw_spi = false;

    // ===== Internal Helpers =====
    void setAddrWindow_internal(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1);
    void sendAddrWindow(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1);
    void sendCommand(uint8_t cmd);
    void sendData8(uint8_t data);
    void sendData16(uint16_t data);
    void writeColor(uint16_t color);
    void resetDisplay();
    void waitBusy();
};